/*************************************************************************************************************************
*\file lienSeriePC.c
*\author Lagreze.A
*\version 1
*\date Juin 2011
*\brief Bibliotheque de fonctions associée au lien série. Basée sur l'utilisation d'une bibliotheque recuperee sur le
* net elle même basée sur les API Windows
*************************************************************************************************************************/
/**
    MODIF 15/01/12 : Ajout d'une variable globale pour le HANDLE du port Série.                        -> Modification sur les déclarations des fonctions de cette bibliothèque.
**/
/* FICHIERS INCLUS*/
/*------------------------------------------------------------------------------*/
#include <windows.h>
#include <winbase.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include "lienSeriePC.h"
#include "gestionTempsPC.h"

/*------------------------------------------------------------------------------
// \func: ouvreCom
//------------------------------------------------------------------------------
// \brief
// Initialise et ouvre un port série
//
//------------------------------------------------------------------------------
// PARAMETRES :
// \param [in] strPort Nom du port "COM1", "COM2"
// \param [in]BaudRate Vitesse
// \param [in] BitsSize Taille de l'info
// \param [in] Parity Parité
// \param [in] StopBits Nombre de bits de stop
// \param [out] le dernier parametre retourne le handler du port serie ouvert (rajoute par A. Lagreze)
// RETOUR :Code d'erreur
//-----------------------------------------------------------------------------*/
e_ErrCom ouvreCom(char *strPort,long BaudRate,int BitsSize,int Parity,int StopBits)
{
    DCB g_DCB; // structure dcb du port

    e_ErrCom g_ErrCom = e_ErrCom_None;

    // On ouvre le port série
    gl_portSer = CreateFile(strPort, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH|FILE_FLAG_NO_BUFFERING, NULL);

    if(gl_portSer == INVALID_HANDLE_VALUE)
    {
        // Echec
        g_ErrCom = e_ErrCom_Creation;
    }
    else
    {
        // On vide les buffers
        PurgeComm(gl_portSer, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);

        // On paramètre le port série
        g_DCB.DCBlength = sizeof(DCB);

        //Configuration actuelle
        GetCommState(gl_portSer, &g_DCB);

        //Modification du DCB
        g_DCB.BaudRate = BaudRate;
        g_DCB.ByteSize = BitsSize;
        g_DCB.Parity = Parity;
        g_DCB.StopBits = StopBits;
        g_DCB.fDtrControl = DTR_CONTROL_DISABLE;

        //Configuration de la liaison serie
        SetCommState(gl_portSer, &g_DCB);
    }
    return g_ErrCom;
}

//----------------------------------------------------------------------------
// FONCTION : emissionCom
//----------------------------------------------------------------------------
// DESCRIPTION :
// Emission d'octets sur la liaison série
//
//----------------------------------------------------------------------------
// PARAMETRES :
// - port  handle du port serie sur lequel on emet (rajoute par A Lagreze)
// -lpBuf Pointeur sur les octets a emettre
// -nCount Nombre d'octet a emettre
//
//----------------------------------------------------------------------------
// RETOUR :Code d'erreur
//----------------------------------------------------------------------------
e_ErrCom emissionCom(const void* lpBuf, unsigned int nCount)
{

    DWORD NumBytes = 0;
    e_ErrCom g_ErrCom ;
    if(gl_portSer != NULL)
    {
        // On pari sur pas d'erreur
        g_ErrCom = e_ErrCom_None;

        //Emission du buffer
        if(WriteFile(gl_portSer, lpBuf, nCount, &NumBytes, NULL) == 0)
        {
            g_ErrCom = e_ErrCom_Emission;
        }
    }
    else
        //Le port n'a pas été ouvert
        g_ErrCom = e_ErrCom_Creation;

    return g_ErrCom;

}

//---------------------------------------------------------------------------
// FONCTION : receptionCom
//---------------------------------------------------------------------------
// DESCRIPTION :
// Reception de caractères sur la liaison série
//
//---------------------------------------------------------------------------
// PARAMETRES :
// - port  handle du port serie sur lequel on recoit (rajoute par A Lagreze)
// -lpBuf Pointeur sur le buffer de caractère a lire
// -nCountMax Nombre maxi de caractère a lire
// -pCountRead Pointeur sur le nombre de caractères lus
//---------------------------------------------------------------------------
// RETOUR :Code d'erreur
//---------------------------------------------------------------------------
e_ErrCom receptionCom(void *lpBuf, unsigned int nCountMax, unsigned int* pCountRead)
{
    COMSTAT Stat;
    DWORD Errors;
    unsigned int nCarALire;
    unsigned long NCarLus = 0;
    e_ErrCom g_ErrCom;

    if(gl_portSer != NULL)
    {
        //on pari sur pas d'erreur
        g_ErrCom = e_ErrCom_None;

        //Pour éviter de gérer un time out
        Sleep(500);

        //Pour connaitre le nombre d'octets dans le buffer d'entrée
        ClearCommError(gl_portSer, &Errors, &Stat);
        nCarALire = Stat.cbInQue;

        //On effectue la lecture si il y a des caractères présents
        if( nCarALire > 0 && nCarALire <= nCountMax )
        {
            if(ReadFile(gl_portSer, lpBuf, nCarALire, &NCarLus, NULL) == 0)
            {
                g_ErrCom = e_ErrCom_Reception;
            }

        }
        *pCountRead = NCarLus;
    }
    else
    //Le port n a pas été ouvert
    g_ErrCom = e_ErrCom_Creation;

    //Compte rendu de l'exécution
    return g_ErrCom;
}

/*---------------------------------------------------------------------------
// \func  : recoitCaractere
//---------------------------------------------------------------------------
// \Brief
// Reception de 1 caractère sur la liaison série avce timeout configurable
//
//---------------------------------------------------------------------------
// \param [in] port handler du port serie sur lequel on reçoit
// \param [in] carRecu  le caractere lu dans le buffer de reception
// \param [in] temspAvantTimeoutEnMilliSec  delai de scrutation avant timeOut en milliSecondes
//---------------------------------------------------------------------------
// \return : enuméré  {CAR_RECU,TIME_OUT,ERREUR_PORT,ERREUR_RECEP}
//---------------------------------------------------------------------------*/
crRet_E recoitCaractere(char* carRecu, int temspAvantTimeoutEnMilliSec)
{
    COMSTAT Stat;
    DWORD Errors;
    unsigned int nCarALire = 0;
    unsigned long nbCarLus = 0;
    carRecu[1] = '\0';

    if(gl_portSer != NULL)
    {
        setWatchDog(temspAvantTimeoutEnMilliSec);
        while(nCarALire == 0 && checkWatchDog() != TIMEOUT)
        {
            ClearCommError(gl_portSer, &Errors, &Stat);
            nCarALire = Stat.cbInQue;
        }
        if (nCarALire > 0)
        {
            /* il y a un caractère disponible dans le buffer de réception */
            if(ReadFile(gl_portSer, carRecu, 1, &nbCarLus, NULL) == 0)
            {
                return ERREUR_RECEP;
            }
            else
            {
                return CARAC_RECU;
            }
        }
        else
            return TIMEOUT;
    }
    else
        return ERREUR_PORT;
}

/*---------------------------------------------------------------------------
// \func  : emetCaractere
//---------------------------------------------------------------------------
// \Brief
// Emission d'un caractère sur le port specifie
//
//---------------------------------------------------------------------------
// \param [in] port handler du port serie sur lequel on reçoit
// \param [in] carAEmettre  le caractere qui sera émis
//---------------------------------------------------------------------------
// \return : rien
//---------------------------------------------------------------------------*/
e_ErrCom emetCaractere(char carAEmettre)
{
    return emissionCom(&carAEmettre, 1) ;
}

/*---------------------------------------------------------------------------
// \func  : fermeCom
//---------------------------------------------------------------------------
// \Brief
// Fermeture du port serie
//
//---------------------------------------------------------------------------
// \param [in] port handler du port serie que l'on veut fermer
//---------------------------------------------------------------------------
// \return : rien
//---------------------------------------------------------------------------*/
void fermeCom()
{
    if(gl_portSer != NULL)
    {
        CloseHandle(gl_portSer);
    }
}

/*---------------------------------------------------------------------------
// \func  : initPortSerie
//---------------------------------------------------------------------------
// \Brief
// Ouverture d'un port serie et l'initialise
//
//---------------------------------------------------------------------------
// \param [in] nom du port a ouvrir
//---------------------------------------------------------------------------
// \return : handler du port ouvert
//---------------------------------------------------------------------------*/
void initPortSerie(char *port)
{
    e_ErrCom g_ErrCom = e_ErrCom_None;
    g_ErrCom = ouvreCom(port, (long)V9600, BITS_8, PAS_DE_PARITE, BIT_DE_STOP_1);

    if (g_ErrCom == e_ErrCom_Creation)
    {
        printf ("Impossible d'ouvrir le port [%s] \n", port);
        getch();
        exit(0);
    }
}

void affichePortCOMDisponibles(void)
{
    char comPort[] = "COM0";
    int i = 0;

    printf("Ports series disponibles :\n");

    for (i=0; i<10; i++)
    {
        comPort[3] += 1;
        if (ouvreCom(comPort, (long)V9600, BITS_8, PAS_DE_PARITE, BIT_DE_STOP_1) == e_ErrCom_None)
        {
            fermeCom();
            printf("%s\n", comPort);
        }
    }

}
